//
//  Daltonizer.m
//  iSeeColor
//
//  Created by Galen Marisco on 10/20/12.
//  Copyright (c) 2012 Wyatt Gosling. All rights reserved.
//

#import "Daltonizer.h"

@implementation Daltonizer
@synthesize imageSize;

- (float*) transformColorspace:(float*) bitmap :(float*) transformVector
{
    float* transformed = malloc(imageSize * sizeof(float));
    for (int i = 0 ; i < imageSize; i += 4) {
        float orig[] = { bitmap[i], bitmap[i+1], bitmap[i+2] };
        float dest[] = { 0, 0, 0 };
        vDSP_mmul( transformVector, 1, orig, 1, dest, 1, 3, 1, 3 );
        
        transformed[i  ] = dest[0];
        transformed[i+1] = dest[1];
        transformed[i+2] = dest[2];
        transformed[i+3] = 255;
    }
    
    return transformed;
}

- (float*) calculateError:(float*) rawBitmap:(float*) colorblindBitmap
{
    float* errorBitmap = malloc(imageSize * sizeof(float));
    
    for (int i = 0; i < imageSize; i += 4) {
        errorBitmap[i  ] = fabs(rawBitmap[i  ] - colorblindBitmap[i  ]);
        errorBitmap[i+1] = fabs(rawBitmap[i+1] - colorblindBitmap[i+1]);
        errorBitmap[i+2] = fabs(rawBitmap[i+2] - colorblindBitmap[i+2]);
        errorBitmap[i+3] = 255; // Hardcode alpha
    }
    
    return errorBitmap;
}

- (float*) correctForShiftedError:(float*) rawBitmap:(float*) shiftedError
{
    float* correctedBitmap = malloc(imageSize * sizeof(float));
    
    for (int i = 0; i < imageSize; i += 4) {
        correctedBitmap[i  ] = MIN(255, rawBitmap[i  ] + shiftedError[i  ]);
        correctedBitmap[i+1] = MIN(255, rawBitmap[i+1] + shiftedError[i+1]);
        correctedBitmap[i+2] = MIN(255, rawBitmap[i+2] + shiftedError[i+2]);
        correctedBitmap[i+3] = 255; // Hardcode alpha
        if ( correctedBitmap[i  ] > 255 ||
            correctedBitmap[i+1] > 255 ||
            correctedBitmap[i+2] > 255 ) {
           // NSLog(@"r:%f g:%f b:%f a:%f", correctedBitmap[i], correctedBitmap[i+1], correctedBitmap[i+2], correctedBitmap[i+3]);
        }
    }
    
    return correctedBitmap;
}

- (float*) convertCharToFloat:(unsigned char*) charBitmap
{
    float* floatBitmap = malloc(imageSize * sizeof(float));
    
    for (int i = 0; i < imageSize; i++) {
        floatBitmap[i] = (float) charBitmap[i];
    }
    
    return floatBitmap;
}

- (unsigned char*) convertFloatToChar:(float*) floatBitmap
{
    unsigned char* charBitmap = malloc(imageSize);
    
    for (int i = 0; i < imageSize; i += 1) {
        charBitmap[i] = MIN((char) roundf(floatBitmap[i]), 255);
    }
    
    return charBitmap;
}

- (void) printFloatArray:(float*) array
{
    NSLog(@"##################### Printing float");
    for (int i = 200 * 1600; i < 201 * 1600; i += 4) {
        NSLog(@"r:%f g:%f b:%f a:%f", array[i], array[i+1], array[i+2], array[i+3]);
    }
}

- (void) printCharArray:(char*) array
{
    NSLog(@"##################### Printing char");
    for (int i = 200 * 1600; i < 201 * 1600; i += 4) {
        NSLog(@"i:%d r:%hhd g:%hhd b:%hhd a:%hhd", i, array[i], array[i+1], array[i+2], array[i+3]);
    }
}

- (float*)defineCorrection:(float*)correctionMatrix:(int)typeOfCB:(float)Intensity{
    if (typeOfCB==1){
        correctionMatrix[0] = 0;
        correctionMatrix[3] = 0.3+Intensity;
        correctionMatrix[6] = 0.3+Intensity;
    }
    else if (typeOfCB==2){
        correctionMatrix[4] = 0;
        correctionMatrix[1] = 0.3+Intensity;
        correctionMatrix[6] = 0.3+Intensity;
    }
    else if (typeOfCB==3){
        correctionMatrix[8] = 0;
        correctionMatrix[1] = 0.3+Intensity;
        correctionMatrix[5] = 0.3+Intensity;
    }
   // NSLog(@"Intensity in funct is: %f", Intensity/1.5);
  //  NSLog(@"Colorblindness in funct is: %d", typeOfCB);
    return correctionMatrix;
}

- (float*)defineCB:(float*)LMScolorMatrix:(int)typeOfCB:(float)Intensity{
    if (typeOfCB==1){
        LMScolorMatrix[0] = 0;
        LMScolorMatrix[1] = 2.02344;
        LMScolorMatrix[2] = -2.52581;
    }
    else if (typeOfCB==2){
        LMScolorMatrix[3] = 0.494207;
        LMScolorMatrix[4] = 0;
        LMScolorMatrix[5] = 1.24827;
    }
    else if (typeOfCB==3){
        LMScolorMatrix[6] = -0.395913;
        LMScolorMatrix[7] = 0.801109;
        LMScolorMatrix[8] = 0;
    }
    return LMScolorMatrix;
}


- (unsigned char*) daltonizeBitmap:(unsigned char*) charBitmap: (int) typeOfCB: (float) Intense
{
    float idMatrix[]  = {1, 0, 0, 0, 1, 0, 0, 0, 1};
    float idMatrix2[]  = {1, 0, 0, 0, 1, 0, 0, 0, 1};
    float rgb2lms[]         = { 17.8824,       43.5161,         4.11935,        3.45565,       27.1554,         3.86714,        0.0299566,          0.184309,       1.46709     };
    float lms2rgb[]         = {  0.0809444479, -0.130504409,    0.116721066,   -0.0102485335,   0.0540193266,  -0.113614708,   -0.000365296938,    -0.00412161469,  0.693511405 };
    
    float* colorblindBitmap = malloc(imageSize * sizeof(float));
    float* finalBitmap      = malloc(imageSize * sizeof(float));
    float* errorBitmap      = malloc(imageSize * sizeof(float));
    float* corrMatrix = idMatrix;
    float* raw2colorblind = idMatrix2;
    
    corrMatrix = [self defineCorrection:corrMatrix:typeOfCB:Intense];
    raw2colorblind = [self defineCB:raw2colorblind:typeOfCB:Intense];
    
    finalBitmap      = [self convertCharToFloat:charBitmap];
    colorblindBitmap = [self convertCharToFloat:charBitmap];
    
    colorblindBitmap = [self transformColorspace:colorblindBitmap :rgb2lms];
    colorblindBitmap = [self transformColorspace:colorblindBitmap :raw2colorblind];
    colorblindBitmap = [self transformColorspace:colorblindBitmap :lms2rgb];
    errorBitmap      = [self calculateError:finalBitmap :colorblindBitmap];
    errorBitmap      = [self transformColorspace:errorBitmap :corrMatrix];
    finalBitmap      = [self correctForShiftedError:colorblindBitmap :errorBitmap];
    
    charBitmap = [self convertFloatToChar:finalBitmap];
    free(colorblindBitmap);
    free(errorBitmap);
    free(finalBitmap);
    
    return charBitmap;
}

- (UIImage*) daltonize:(UIImage*) image
{
    CGContextRef    ctx;
    CGImageRef      imageRef   = [image CGImage];
    NSUInteger      width      = CGImageGetWidth(imageRef);
    NSUInteger      height     = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    imageSize = height * width * 4;
    unsigned char* charBitmap = malloc(imageSize);
    
    NSUInteger   bytesPerPixel    = 4;
    NSUInteger   bytesPerRow      = bytesPerPixel * width;
    NSUInteger   bitsPerComponent = 8;
    CGContextRef context          = CGBitmapContextCreate(charBitmap, width, height,
                                                          bitsPerComponent, bytesPerRow, colorSpace,
                                                          kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    
    CGColorSpaceRelease(colorSpace);
    CGContextRelease(context);
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    int colorBlindnessType = [defaults integerForKey:@"colorBlindnessType"];
    float intensity = [defaults floatForKey:@"intensity"];
   // NSLog(@"Intensity is: %f", intensity/1.5);
 //   NSLog(@"Colorblindness is: %d", colorBlindnessType);
    
    charBitmap = [self daltonizeBitmap:charBitmap:colorBlindnessType:intensity];
    
    ctx = CGBitmapContextCreate(charBitmap,
                                CGImageGetWidth(  imageRef ),
                                CGImageGetHeight( imageRef ),
                                8,
                                CGImageGetBytesPerRow( imageRef ),
                                CGImageGetColorSpace(  imageRef ),
                                kCGImageAlphaPremultipliedLast );
    
    imageRef = CGBitmapContextCreateImage (ctx);
    UIImage* finalImage = [UIImage imageWithCGImage:imageRef];
    
    CGContextRelease(ctx);
    
    return finalImage;
}

@end
